package com.zhisheng.core.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.zhisheng.core.dao.GenericDAO;
import com.zhisheng.core.page.Page;

/**
 * @author qzhou
 * @since 2008-4-9
 * 
 */
@SuppressWarnings("unchecked")
public abstract class BaseDaoHibernate<T,ID extends Serializable> extends HibernateDaoSupport implements GenericDAO<T, ID>{
	
	private Class<T> persistentClass;
	
	@Autowired
	public void setMySessionFactory(SessionFactory sessionFactory) {
		setHibernateTemplate(createHibernateTemplate(sessionFactory));
	}
   
	public BaseDaoHibernate() {
        this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                                .getGenericSuperclass()).getActualTypeArguments()[0];
    }
	
	public List<T> findAll() {
		return getHibernateTemplate().find("from " + persistentClass.getName());
	}
	
	public <E>List<E> findAll(Class<E> c) {
		return getHibernateTemplate().find("from " + c.getName());
	}
	
	public <S> S findEntityById(Serializable id, Class<S> c) {
		return (S) getHibernateTemplate().get(c, id);
	}
	
	public List findByHql(String hql) {
		return getHibernateTemplate().find(hql);
	}
	
	/**
	 * 根据id查询对应实体
	 */
	public T findById(ID id) {
		return (T) getHibernateTemplate().get(persistentClass, new Integer(id.toString()));
	}
	
	/**
	 * 根据id查询对应实体
	 */
	public T loadById(ID id) {
		return (T) getHibernateTemplate().load(persistentClass, id);
	}

	/**
     * 基本的保存操作
     */
    public void save(Object entity){
    	getHibernateTemplate().save(entity);
    }

    /**
     * 基本的更新操作
     */
    public void update(Object entity){
    	getHibernateTemplate().update(entity);
    }
    
    public void saveOrUpdate(Object o) {
    	getHibernateTemplate().saveOrUpdate(o);
    }
    
    public void saveOrUpdateAll(List<T> list) {
    	getHibernateTemplate().saveOrUpdateAll(list);
    }
    /**
     * 基本的删除操作
     */
    public void delete(Object entity){
    	getHibernateTemplate().delete(entity);
    }
    
    /**
     * 基本的删除操作
     */
    public void delete(String id){
//    	getHibernateTemplate().update("delete from " +persistentClass.getName()+ " a where a.id=?", id);
    	Object entity = getHibernateTemplate().get(persistentClass, new Integer(id));
    	getHibernateTemplate().delete(entity);
    }
    
    @SuppressWarnings("unchecked")
	public void delete(Class c, Serializable id) {
    	Object entity = getHibernateTemplate().get(c, id);
    	getHibernateTemplate().delete(entity);
    }

    /**
     * 基本的分页操作，不附带任何查询条件
     */
    @SuppressWarnings("unchecked")
	public Page findByPage(Page page, Class c){
    	Session session = getSession();
    	Criteria criteria = null;
    	if (c == null) {
    		criteria = session.createCriteria(persistentClass);
    	} else {
    		criteria = session.createCriteria(c);
    	}
    	return findPageByCriteria(criteria, page);
    }
    
    /**
     * 这个是利用 criteria 作分页查询的主方法，
     * 
     * @see Pagination
     * @param criteria
     * @param pagination
     * @return pagination
     */
    public Page findPageByCriteria(final Criteria criteria, Page page) {
  
            criteria.setProjection(Projections.rowCount());
            Integer iCount = Integer.valueOf(criteria.uniqueResult().toString());
            int totalCount = iCount.intValue();
            criteria.setProjection(null);
            criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
            page.setTotalRecords(totalCount);
   
           criteria.setFirstResult(page.getListStartIndex())
                    .setMaxResults(page.getPageSize());

		    if (!StringUtils.isEmpty(page.getSortType())) {
		        if (Page.SORT_DESC.equals(page.getSortType())) {
		            criteria.addOrder(Order.desc(page.getSortType()));
		        } else {
		            criteria.addOrder(Order.asc(page.getSortType()));
		        }
		    }

		    List items = criteria.list();
		    page.setItems(items);
		
		    return page;
    }
        
    public Page findPageByHql(String hql, Map<String, Object> parameters, Page page) {
        List items = searchForList(hql, parameters, page);
        int icount = searchForInt(hql, parameters);
        page.setTotalRecords(icount);
        page.setItems(items);
        return page;
    }
    
    private List searchForList(final String sentence, final Map<String, Object> parameters, final Page page) {
		return (List) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Query query = session.createQuery(sentence);
				query.setFirstResult(page.getListStartIndex());
				query.setMaxResults(page.getPageSize());
				if(parameters!=null){
					for (Iterator iter = parameters.entrySet().iterator(); iter.hasNext();) {
						Map.Entry entry = (Map.Entry) iter.next();
						Object value = entry.getValue();
						if (value instanceof List) {
							query.setParameterList((String) entry.getKey(), (List) value);
						} else {
							query.setParameter((String) entry.getKey(), value);
						}
					}
				}
				return query.list();
			}
		}, true);
	}
    
    private int searchForInt(final String sentence, final Map<String, Object> parameters) {
    	
		List result = (List) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Query query = session.createQuery(sentence);
				for (Iterator iter = parameters.entrySet().iterator(); iter.hasNext();) {
					Map.Entry entry = (Entry) iter.next();
					Object value = entry.getValue();
					if (value instanceof List) {
						query.setParameterList((String) entry.getKey(), (List) value);
					} else {
						query.setParameter((String) entry.getKey(), value);
					}
				}
				return query.list(); 
			}
		}, true);
		
		
		if(result==null||result.size()==0){
			return 0;
		}
		// deal with the condition when hql contains group by
		return result.size();
	}
    
    /*------------------工具方法---------------------
     * 这些方法是criteria所没有的，所以我放在BaseDaoHibernate来做这些操作，显然目前这些方法还是不够的，
     * 等用到什么时可以继续添加
     * add by ahuaxuan
     * */
    
    /**
     * 设置相等的查询操作，如果是String则需要判断是否为空字符串，如果不是String且不为空，那么就设置其为查询条件
     */
    public void eqAndIsNotBlank(String property, Criteria criteria, Object condition) {
    	if (null != condition) {
    		if (condition instanceof String) {
    			if (StringUtils.isNotBlank((String)condition)) {
    				criteria.add(Restrictions.eq(property, condition));
    			}
    		} else {
    			criteria.add(Restrictions.eq(property, condition));
    		}
    	}
    }
    
    /**
     *  设置like的查询操作,如果是String则需要判断是否为空字符串，如果不是String且不为空，那么就设置其为查询条件
     * @param property
     * @param criteria
     * @param condition
     */
    public void likeAndIsNotBlank(String property, Criteria criteria, Object condition) {
    	if (null != condition) {
    		if (condition instanceof String) {
    			if (StringUtils.isNotBlank((String)condition)) {
    				criteria.add(Restrictions.ilike(property, (String)condition,MatchMode.ANYWHERE));
    			}
    		} else {
    			criteria.add(Restrictions.ilike(property, condition));
    		}
    	}
    }
    
    /**
     * 设置大于的查询操作，如果是String则需要判断是否为空字符串，如果不是String且不为空，那么就设置其为查询条件
     */
    public void gtAndIsNotBlank(String property, Criteria criteria, Object condition) {
    	if (null != condition) {
    		if (condition instanceof String) {
    			if (StringUtils.isNotBlank((String)condition)) {
    				criteria.add(Restrictions.gt(property, condition));
    			}
    		} else {
    			criteria.add(Restrictions.gt(property, condition));
    		}
    	}
    }
    
    /**
     * 设置大于等于的查询操作，如果是String则需要判断是否为空字符串，如果不是String且不为空，那么就设置其为查询条件
     */
    public void geAndIsNotBlank(String property, Criteria criteria, Object condition) {
    	if (null != condition) {
    		if (condition instanceof String) {
    			if (StringUtils.isNotBlank((String)condition)) {
    				criteria.add(Restrictions.ge(property, condition));
    			}
    		} else {
    			criteria.add(Restrictions.ge(property, condition));
    		}
    	}
    }
    
    /**
     * 设置小于的查询操作，如果是String则需要判断是否为空字符串，如果不是String且不为空，那么就设置其为查询条件
     */
    public void ltAndIsNotBlank(String property, Criteria criteria, Object condition) {
    	if (null != condition) {
    		if (condition instanceof String) {
    			if (StringUtils.isNotBlank((String)condition)) {
    				criteria.add(Restrictions.lt(property, condition));
    			}
    		} else {
    			criteria.add(Restrictions.lt(property, condition));
    		}
    	}
    }
    
    /**
     * 设置小于等于的查询操作，如果是String则需要判断是否为空字符串，如果不是String且不为空，那么就设置其为查询条件
     */
    public void leAndIsNotBlank(String property, Criteria criteria, Object condition) {
    	if (null != condition) {
    		if (condition instanceof String) {
    			if (StringUtils.isNotBlank((String)condition)) {
    				criteria.add(Restrictions.le(property, condition));
    			}
    		} else {
    			criteria.add(Restrictions.le(property, condition));
    		}
    	}
    }
    
    public Object findByQueryUniqueResult(final String hql,final Object values[]){
    	return getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(org.hibernate.Session session)
					throws HibernateException, SQLException {
				
				Query query = session.createQuery(hql);
				if (values != null) {
					for (int i = 0; i < values.length; i++){
						query.setParameter(i, values[i]);
					}
				}
				return query.uniqueResult();
			}
		});
    }

    /**
     * Execute an HQL query, binding one condition to a "?" parameter in the query string.
     * 
     * @param queryString
     * @param condition
     * @return
     * @see {@link #getHibernateTemplate()#find(queryString, value)}
     */
    public <E> List<E> findByCondition(final String queryString, final Object condition)
    {
        return getHibernateTemplate().find(queryString, condition);
    }

    /**
     * Execute an HQL query, binding a number of conditions to "?" parameters in the query string.
     * 
     * @param queryString
     * @param conditions
     * @return
     * @see {@link #getHibernateTemplate()#find(queryString, values)}
     */
    public <E> List<E> findByCondition(final String queryString, final Object... conditions)
    {
        return getHibernateTemplate().find(queryString, conditions);
    }
}

